/** @param { number } value */
function formatInt(value) {
    return value >= 0 ? value : 0x100000000 + value;
}

/** IP地址类 */
export default class IpAddress {

    /** @param { string } ip */
    static parseIp(ip) {
        const info = new IpAddress();
        info.ip = ip;
        return info;
    }

    /** @param { number } value */
    static parseDec(value) {
        const info = new IpAddress();
        info.dec = value;
        return info;
    }

    /** @param { string } hex */
    static parseHex(hex) {
        const info = new IpAddress();
        info.hex = hex;
        return info;
    }

    /** @param { string } bin */
    static parseBin(bin) {
        const info = new IpAddress();
        info.bin = bin;
        return info;
    }

    /** @param { string } mask */
    static parseMask(mask) {
        if (mask.includes(".")) {
            return IpAddress.parseIp(mask);
        } else {
            return IpAddress.parseDec(parseInt("1".repeat(parseInt(mask)).padEnd(32, "0"), 2));
        }
    }

    /**
     * @param { IpAddress } ip
     * @param { IpAddress } mask
     */
    static getNetwork(ip, mask) {
        return IpAddress.parseDec(ip.dec & mask.dec);
    }

    /**
     * @param { IpAddress } ip
     * @param { IpAddress } mask
     */
    static getBroadcast(ip, mask) {
        return IpAddress.parseDec(ip.dec | formatInt(mask.dec ^ 0xFFFFFFFF));
    }

    /**
     * @param { IpAddress } ip
     * @param { IpAddress } mask
     */
    static getMaskRange(ip, mask) {
        const network = IpAddress.getNetwork(ip, mask);
        const broadcast = IpAddress.getBroadcast(ip, mask);
        const count = broadcast.dec - network.dec - 1;
        const first = network.addDec(1);
        const end = broadcast.addDec(-1);
        return {
            count,
            network,
            first,
            end,
            broadcast,
        };
    }

    constructor() {
        this._value = 0;
    }

    get dec() {
        return this._value;
    }
    set dec(value) {
        this._value = formatInt(value);
    }

    get list() {
        let value = this._value;
        const save = [];
        for (let i = 0; i < 4; i++) {
            save.unshift(value % 256);
            value = Math.floor(value / 256);
        }
        return save;
    }
    set list(list) {
        this._value = list.reduce((count, num, i) => count + num * Math.pow(256, i), 0);
    }

    get ip() {
        return this.list.map(item => item.toString()).join(".");
    }
    set ip(ip) {
        this.list = ip.split(".").map(item => parseInt(item)).reverse();
    }

    get hex() {
        return this.list.map(num => num.toString(16).padStart(2, "0")).join(".");
    }
    set hex(hex) {
        this.list = hex.split(".").map(str => parseInt(str, 16)).reverse();
    }

    get bin() {
        return this.list.map(num => num.toString(2).padStart(8, "0")).join(".");
    }
    set bin(bin) {
        this.list = bin.split(".").map(str => parseInt(str, 2)).reverse();
    }

    /** @param { number } num */
    addDec(num) {
        return IpAddress.parseDec(this.dec + num);
    }

}
